package com.lyh.framework.web.service;

import javax.annotation.Resource;

import com.lyh.common.enums.IncomeEnum;
import com.lyh.common.utils.uuid.IdUtils;
import com.lyh.system.domain.*;
import com.lyh.system.domain.req.SysMemberUserPageReq;
import com.lyh.system.service.*;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.lyh.common.constant.CacheConstants;
import com.lyh.common.constant.Constants;
import com.lyh.common.constant.UserConstants;
import com.lyh.common.core.domain.entity.SysUser;
import com.lyh.common.core.domain.model.LoginUser;
import com.lyh.common.core.redis.RedisCache;
import com.lyh.common.exception.ServiceException;
import com.lyh.common.exception.user.BlackListException;
import com.lyh.common.exception.user.CaptchaException;
import com.lyh.common.exception.user.CaptchaExpireException;
import com.lyh.common.exception.user.UserNotExistsException;
import com.lyh.common.exception.user.UserPasswordNotMatchException;
import com.lyh.common.utils.DateUtils;
import com.lyh.common.utils.MessageUtils;
import com.lyh.common.utils.StringUtils;
import com.lyh.common.utils.ip.IpUtils;
import com.lyh.framework.manager.AsyncManager;
import com.lyh.framework.manager.factory.AsyncFactory;
import com.lyh.framework.security.context.AuthenticationContextHolder;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Member;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 登录校验方法
 *
 * @author lyh
 */
@Component
public class SysLoginService {
    @Autowired
    private TokenService tokenService;

    // 令牌秘钥
    @Value("${token.secret}")
    private String secret;

    // 是否允许账户多终端同时登录（true允许 false不允许）
    @Value("${token.soloLogin}")
    private boolean soloLogin;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private ISysMemberUserService memberUserService;

    @Autowired
    private IInvitationService invitationService;

    @Autowired
    private IMemberInvitationService memberInvitationService;

    @Autowired
    private IAssetService assetService;

    @Autowired
    private IMemberPurchaseInfoService memberPurchaseInfoService;

    @Autowired
    private IIncomeService incomeService;


    public String loginByCode(String telephone, String code) {
        //validateCaptcha(telephone, code);
        //验证用户
        SysUser sysUser = userService.selectUserByTelephone(telephone);
        //等待短信验证码上线
        if (sysUser == null) {
            throw new UserNotExistsException();
        }
        String username = sysUser.getUserName();
        Authentication authentication = null; // 用户验证
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, Constants.CUSTOM_LOGIN_SMS);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                throw new UserPasswordNotMatchException();          //抛出账号或者密码错误的异常
            } else {
                throw new ServiceException(e.getMessage());         //抛出其他异常
            }
        } finally {
            AuthenticationContextHolder.clearContext();
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());                     //修改sys_user最近登录IP和登录时间
        // 生成token
        return tokenService.createToken(loginUser);
    }

    /**
     * 密码验证
     *
     * @param username 用户名
     * @param password 密码
     * @return 结果
     */
    public String login(String username, String password) {
        // 登录前置校验
        loginPreCheck(username, password);
        // 用户验证
        Authentication authentication = null;
        //根据手机号
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            } else {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        } finally {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        if (!soloLogin) {
            // 如果用户不允许多终端同时登录，清除缓存信息
            String userIdKey = Constants.LOGIN_USERID_KEY + loginUser.getUser().getUserId();
            String userKey = redisCache.getCacheObject(userIdKey);
            if (StringUtils.isNotEmpty(userKey)) {
                redisCache.deleteObject(userIdKey);
                redisCache.deleteObject(userKey);
            }
        }
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }

    /**
     * 校验验证码
     *
     * @param telephone 用户名
     * @param code      验证码
     * @return 结果
     */
    public void validateCaptcha(String telephone, String code) {

        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + telephone;
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null) {
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha)) {
            throw new CaptchaException();
        }

//        boolean captchaEnabled = configService.selectCaptchaEnabled();
//        if (captchaEnabled)
//        {
//
//        }
    }

    /**
     * 登录前置校验
     *
     * @param username 用户名
     * @param password 用户密码
     */
    public void loginPreCheck(String username, String password) {
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // IP黑名单校验
        String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
            throw new BlackListException();
        }
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr());
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }

    public String appLoginByCode(String mobile, String code) {
//        validateCaptcha(mobile,code);
        //验证用户
        SysMemberUser member = memberUserService.selectMemberByMobile(mobile);
        //等待短信验证码上线

        if (member == null) {
            throw new UserNotExistsException();
        }
        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, IdUtils.fastUUID());
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        redisCache.setCacheObject(member.getId().toString(), token, 6, TimeUnit.HOURS);
        return token;
    }

    /**
     * APP验证码登录方法
     *
     * @param mobile 手机号
     * @param code   验证码
     * @return 结果
     */
    @Transactional
    public String appCodeLogin(String mobile, String code, String invitationCode) {
//        validateCaptcha(username, code); //校验验证码
        Authentication authentication = null; // 用户验证
        SysUser user = userService.selectUserByTelephone(mobile);
        SysMemberUser sysMemberUser = memberUserService.selectMemberByUserId(user.getUserId());
        SysUser sysUser = userService.selectUserById(sysMemberUser.getUserId());
        if (sysMemberUser == null || sysUser == null) {
            throw new UserNotExistsException();
        }
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUserName(), Constants.CUSTOM_LOGIN_SMS);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                throw new UserPasswordNotMatchException();          //抛出账号或者密码错误的异常
            } else {
                throw new ServiceException(e.getMessage());         //抛出其他异常
            }
        } finally {
            AuthenticationContextHolder.clearContext();
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());                     //修改sys_user最近登录IP和登录时间
        // 生成token
        String token = tokenService.createToken(loginUser);

        if (invitationCode != null && !"".equals(invitationCode)) {
            // 直推 ------- 登录成功,判断邀请码，以及推广信息，充值余额 --- 产生推广收益
            SysMemberUser memberUser = memberUserService.selectSysMemberUserByCode(invitationCode);
            if (memberUser != null) {

                //记录推广信息
                Invitation invitation = new Invitation();
                invitation.setMemberId(memberUser.getId());
                invitation.setInvitationCode(invitationCode);
                invitation.setInviteeCode(sysMemberUser.getInvitationCode());
                invitationService.insertInvitation(invitation);

                Asset asset = assetService.selectAssetByMemberId(memberUser.getId());
                List<MemberInvitation> memberInvitations = memberInvitationService.selectMemberInvitationByMemberId(memberUser.getId());
                if (memberInvitations != null && memberInvitations.size() > 0) {
                    MemberInvitation memberInvitation = memberInvitations.get(0);
                    BigDecimal amount = memberInvitation.getAmount();
                    //每个用户最多推广三次，将金额返完
                    switch (memberInvitation.getRemainingPromotionTimes().intValue()) {
                        case 3:
                            amount = amount.multiply(new BigDecimal(0.2));
                            break;
                        case 2:
                            amount = amount.multiply(new BigDecimal(0.3));
                            break;
                        case 1:
                            amount = amount.multiply(new BigDecimal(0.5));
                            break;
                        default:
                            throw new RuntimeException("参数非法！");
                    }

                    BigDecimal accountBalance = asset.getAccountBalance().add(amount);
                    asset.setAccountBalance(accountBalance);
                }
                // 直推 ------------- 预约报单，动态收益 直推千分之2.5
                List<MemberPurchaseInfo> memberPurchaseInfoList = memberPurchaseInfoService.selectPurchaseInfoByMemberId(memberUser.getId());
                if (memberPurchaseInfoList != null && memberPurchaseInfoList.size() > 0) {
                    BigDecimal num = new BigDecimal(0.0000);
                    //动态收益 - 记录
                    Income income = new Income();
                    income.setUserId(memberUser.getId());
                    for (MemberPurchaseInfo memberPurchaseInfo : memberPurchaseInfoList) {
                        //动态收益 直推千分之2.5
                        BigDecimal assetEvidenceNum = memberPurchaseInfo.getAssetEvidenceNum();
                        BigDecimal multiply = assetEvidenceNum.multiply(new BigDecimal(0.0025));
                        //权益额度不足，不加收益
                        if (asset.getEquityLimit().compareTo(multiply) >= 0) {
                            num = num.add(multiply);

                            income.setType(IncomeEnum.DYNAMIC.getTitle());
                            income.setIncomeNum(multiply);
                            income.setIncomeDate(new Date());
                            incomeService.insertIncome(income);
                        }
                    }
                    BigDecimal bigDecimal = asset.getAssetEvidenceNum().add(num);
                    BigDecimal subtract = asset.getEquityLimit().subtract(num);
                    BigDecimal add = asset.getAssetEvidenceNum().add(bigDecimal);
                    asset.setAssetEvidenceNum(add);
                    asset.setEquityLimit(subtract);
                }
                assetService.updateAsset(asset);
            }

            // 间推 ---------------- 预约报单，动态收益 间推千分之1.5
            List<Long> memberIds = invitationService.selectInviteeMemberIds(invitationCode);
            for (Long memberId : memberIds) {
                List<MemberPurchaseInfo> memberPurchaseInfos = memberPurchaseInfoService.selectPurchaseInfoByMemberId(memberId);
                if (memberPurchaseInfos != null && memberPurchaseInfos.size() > 0) {
                    Asset asset = assetService.selectAssetByMemberId(memberId);
                    BigDecimal num = new BigDecimal(0.0000);
                    //动态收益 - 记录
                    Income income = new Income();
                    income.setUserId(memberId);
                    for (MemberPurchaseInfo memberPurchaseInfo : memberPurchaseInfos) {

                        //动态收益 间推千分之1.5
                        BigDecimal assetEvidenceNum = memberPurchaseInfo.getAssetEvidenceNum();
                        BigDecimal multiply = assetEvidenceNum.multiply(new BigDecimal(0.0015));
                        num = num.add(multiply);

                        income.setType(IncomeEnum.DYNAMIC.getTitle());
                        income.setIncomeNum(multiply);
                        income.setIncomeDate(new Date());
                        incomeService.insertIncome(income);
                    }
                    BigDecimal bigDecimal = asset.getAssetEvidenceNum().add(num);
                    BigDecimal add = asset.getAssetEvidenceNum().add(bigDecimal);
                    asset.setAssetEvidenceNum(add);
                    assetService.updateAsset(asset);
                }
            }
        }

        return token;
    }
}
